/*
 * Copyright (C) 2021 XRADIO TECHNOLOGY CO., LTD. All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *    1. Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the
 *       distribution.
 *    3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of
 *       its contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#ifndef __XR_OHOS_UTILS_H__
#define __XR_OHOS_UTILS_H__

#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include <stdbool.h>
//
#include "ohos_bt_def.h"
#include "ohos_bt_gatt.h"
#include "ohos_bt_gatt_server.h"

#include "framework/bt_ctrl.h"
//
#include <zephyr.h>
#include <settings/settings.h>
#include <bluetooth/hci.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/conn.h>
#include "ble/sys/util.h"
#include "ble/sys/byteorder.h"
//
#include "common/framework/bt_ctrl.h"
//

// for code
#ifndef CONFIG_BT_CONN
#define CONFIG_BT_CONN (1)
#define CONFIG_BT_SMP (1)
#define CONFIG_BT_OBSERVER (1)
#define CONFIG_BT_EXT_ADV (1)
#define CONFIG_BT_USER_PHY_UPDATE (1)
#define CONFIG_BT_BREDR (1)
#define CONFIG_BT_SM (1)
#define CONFIG_BT_REMOTE_INFO (1)
#define CONFIG_BT_USER_DATA_LEN_UPDATE (1)
#define CONFIG_BT_BROADCASTER (1)
#define CONFIG_BT_PERIPHERAL (1)
#define CONFIG_BT_PER_ADV_SYNC (1)
#define CONFIG_BT_EXT_ADV_MAX_ADV_SET 5
#endif

#define BT_ID_DEFAULT (0)
#define BT_ADDR_LE_STR_LEN 20
// for debug

#define XR_BLE_LOG_LEVEL 3
#if 1
#define XR_BLE_LOG(format, ...) printf(format "\n", ##__VA_ARGS__)
#elif 0
#define XR_BLE_LOG(format, ...)                                                \
	printf("\033[1;34m" format "\n\033[0m", ##__VA_ARGS__)
#endif
#define XR_BLE_DBG(msg, ...)                                                   \
	do {                                                                   \
		if (XR_BLE_LOG_LEVEL > 2)                                      \
			XR_BLE_LOG("[xr ble] DBG " msg, ##__VA_ARGS__);        \
	} while (0)
#define XR_BLE_WAR(msg, ...)                                                   \
	do {                                                                   \
		if (XR_BLE_LOG_LEVEL > 1)                                      \
			XR_BLE_LOG("[xr ble] WAR " msg, ##__VA_ARGS__);        \
	} while (0)
#define XR_BLE_ERR(msg, ...)                                                   \
	do {                                                                   \
		if (XR_BLE_LOG_LEVEL > 0)                                      \
			XR_BLE_LOG("[xr ble] ERR " msg, ##__VA_ARGS__);        \
	} while (0)

#define XR_GATT_DBG(msg, ...)                                                  \
	do {                                                                   \
		if (XR_BLE_LOG_LEVEL > 2)                                      \
			XR_BLE_LOG("[xr gatt] DBG " msg, ##__VA_ARGS__);       \
	} while (0)
#define XR_GATT_ERR(msg, ...)                                                  \
	do {                                                                   \
		if (XR_BLE_LOG_LEVEL > 0)                                      \
			XR_BLE_LOG("[xr gatt] ERR " msg, ##__VA_ARGS__);       \
	} while (0)

static inline int OHOS_ADDR_2_XRBLE_ADDR(const BdAddr *bdAddr,
					 bt_addr_le_t *addr)
{
	addr->a.val[0] = bdAddr->addr[0];
	addr->a.val[1] = bdAddr->addr[1];
	addr->a.val[2] = bdAddr->addr[2];
	addr->a.val[3] = bdAddr->addr[3];
	addr->a.val[4] = bdAddr->addr[4];
	addr->a.val[5] = bdAddr->addr[5];
	if (BT_ADDR_IS_RPA(&(addr->a)) || BT_ADDR_IS_NRPA(&(addr->a)) ||
	    BT_ADDR_IS_STATIC(&(addr->a))) {
		addr->type = BT_ADDR_LE_RANDOM;
	} else {
		addr->type = BT_ADDR_LE_PUBLIC;
	}
	return 0;
}

static inline int XRBLE_ADDR_2_OHOS_ADDR(const bt_addr_le_t *addr,
					 BdAddr *bdAddr)
{
	bdAddr->addr[0] = addr->a.val[0];
	bdAddr->addr[1] = addr->a.val[1];
	bdAddr->addr[2] = addr->a.val[2];
	bdAddr->addr[3] = addr->a.val[3];
	bdAddr->addr[4] = addr->a.val[4];
	bdAddr->addr[5] = addr->a.val[5];
	return 0;
}

static inline int
OHOS_ADV_PARAM_2_XRBLE_LEGACY_ADV_PARAM(BleAdvParams *param,
					struct bt_le_adv_param *adv_param)
{
#if defined(CONFIG_BT_BROADCASTER)
	// #if defined(CONFIG_BT_EXT_ADV)
	// struct bt_le_adv_param adv_param = {};
	int selected_id = 0;
	if (param == NULL || adv_param == NULL) {
		return 1;
	}

	adv_param->options = (BT_LE_ADV_OPT_NONE);
	adv_param->id = selected_id;
	adv_param->interval_min = (uint16_t)(param->minInterval); //todo
	adv_param->interval_max = (uint16_t)(param->maxInterval); //todo

	switch (param->advType) {
	case OHOS_BLE_ADV_IND: /** Connectable and scannable undirected advertisement (default) */
		adv_param->options |= BT_LE_ADV_OPT_CONNECTABLE;
		adv_param->options |= BT_LE_ADV_OPT_SCANNABLE;
		break;
	case OHOS_BLE_ADV_DIRECT_IND_HIGH: /** Connectable directed advertisement with a high duty ratio */
		adv_param->options |= BT_LE_ADV_OPT_CONNECTABLE;
		break;
	case OHOS_BLE_ADV_SCAN_IND: /** Scannable undirected advertisement */
		adv_param->options |= BT_LE_ADV_OPT_SCANNABLE;
		break;
	case OHOS_BLE_ADV_NONCONN_IND: /** Unconnectable undirected advertisement */
		adv_param->options |= BT_LE_ADV_OPT_SCANNABLE;
		break;
	case OHOS_BLE_ADV_DIRECT_IND_LOW: /** Connectable directed advertisement with a low duty ratio */
		adv_param->options |= BT_LE_ADV_OPT_CONNECTABLE;
		adv_param->options |= BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY;
		break;
	default:
		break;
	}
	if (param->advType == OHOS_BLE_ADV_DIRECT_IND_HIGH ||
	    param->advType == OHOS_BLE_ADV_DIRECT_IND_LOW) {
		param->peerAddrType;
		param->peerAddr; //
	}

	param->ownAddrType;

	{
		if (!(param->channelMap & 0x01)) {
			adv_param->options |= BT_LE_ADV_OPT_DISABLE_CHAN_37;
		}
		if (!(param->channelMap & 0x02)) {
			adv_param->options |= BT_LE_ADV_OPT_DISABLE_CHAN_38;
		}
		if (!(param->channelMap & 0x04)) {
			adv_param->options |= BT_LE_ADV_OPT_DISABLE_CHAN_39;
		}
	}
	//todo

	/**
	 * Advertising channel to be used. For example, <b>0x01</b> indicates that channel 37 is to be used,
	 * and <b>0x07</b> indicates that channels 37, 38, and 39 are used.
	 */
	param->advFilterPolicy; //
	param->txPower;
	// param->options |= BT_LE_ADV_OPT_USE_TX_POWER;
	param->duration;

// #endif /* CONFIG_BT_EXT_ADV */
#endif /* CONFIG_BT_BROADCASTER */
	return 0;
}

// for temporary use
static inline int OHOS_ADV_DATA_2_XRBLE_ADV_DATA(const BleConfigAdvData *data,
						 struct bt_data *ad,
						 size_t *ad_len,
						 struct bt_data *sd,
						 size_t *sd_len)
{
	struct bt_data *addata;
	size_t *data_len;
	int len;
	unsigned short hex_data_len;
	uint8_t *hexdata;
	if (data->advLength && data->advData != NULL) {
		data = ad;
		data_len = ad_len;
		(*data_len) = 0;
		hex_data_len = 0;
		hexdata = (uint8_t *)data->advData;
		while (hex_data_len < data->advLength) {
			addata[*data_len].type = hexdata[hex_data_len + 1];
			addata[*data_len].data_len =
				hexdata[hex_data_len] - 1; //todo
			addata[*data_len].data = &hexdata[hex_data_len + 2];
			(*data_len)++;
			hex_data_len += hexdata[hex_data_len] + 1;
		}
	}
	if (data->scanRspLength && data->scanRspData != NULL) {
		data = sd;
		data_len = sd_len;
		(*data_len) = 0;
		hex_data_len = 0;
		hexdata = (uint8_t *)data->scanRspData;
		while (hex_data_len < data->advLength) {
			addata[*data_len].type = hexdata[hex_data_len + 1];
			addata[*data_len].data_len =
				hexdata[hex_data_len] - 1; //todo
			addata[*data_len].data = &hexdata[hex_data_len + 2];
			(*data_len)++;
			hex_data_len += hexdata[hex_data_len] + 1;
		}
	}
	return 0;
}

static inline int
OHOS_SCAN_PARAM_2_XRBLE_SCAN_PARAM(BleScanParams *param,
				   struct bt_le_scan_param *scan_param)
{
#if defined(CONFIG_BT_OBSERVER)
	if (!param || !scan_param) {
		return 0;
	}
	//init scan_param
	scan_param->type = BT_LE_SCAN_TYPE_ACTIVE;
	scan_param->options = BT_LE_SCAN_OPT_FILTER_DUPLICATE;
	scan_param->interval = BT_GAP_SCAN_FAST_INTERVAL;
	scan_param->window = BT_GAP_SCAN_FAST_WINDOW;
	scan_param->timeout = 0;

	if (param->scanType == OHOS_BLE_SCAN_TYPE_ACTIVE) {
		scan_param->type = BT_LE_SCAN_TYPE_ACTIVE;
	} else if (param->scanType == OHOS_BLE_SCAN_TYPE_PASSIVE) {
		scan_param->type = BT_LE_SCAN_TYPE_PASSIVE;
		scan_param->options = BT_LE_SCAN_OPT_NONE;
	}

	scan_param->interval =
		(uint16_t)(param->scanInterval); //BT_GAP_SCAN_FAST_INTERVAL;
	scan_param->window =
		(uint16_t)(param->scanWindow); //BT_GAP_SCAN_FAST_WINDOW;

	if (!(param->scanPhy & 0x01)) {
		scan_param->options |= BT_LE_SCAN_OPT_NO_1M;
	}
	if (param->scanPhy & 0x02) {
		scan_param->options |= BT_LE_SCAN_OPT_CODED;
	}

	switch (param->scanFilterPolicy) {
	case OHOS_BLE_SCAN_FILTER_POLICY_ACCEPT_ALL:
		break;
	case OHOS_BLE_SCAN_FILTER_POLICY_ONLY_WHITE_LIST:
		scan_param->options |= BT_LE_SCAN_OPT_FILTER_WHITELIST;
		break;
	case OHOS_BLE_SCAN_FILTER_POLICY_ACCEPT_ALL_AND_RPA:
		//todo
		break;
	case OHOS_BLE_SCAN_FILTER_POLICY_ONLY_WHITE_LIST_AND_RPA:
		//todo
		break;
	default:
		break;
	}
#endif /* CONFIG_BT_OBSERVER */
	return 0;
}

static inline int
SCAN_RESULT_DATA_PARASE(BtScanResultData *pscanResultdata,
			const struct bt_le_scan_recv_info *info)
{
	BtScanResultData scanResultdata;
	XRBLE_ADDR_2_OHOS_ADDR(info->addr, &(pscanResultdata->addr));
	pscanResultdata->rssi = info->rssi;
	pscanResultdata->txPower = info->tx_power;
	pscanResultdata->periodicAdvInterval = info->interval;
	pscanResultdata->advSid = info->sid; //todo check
	pscanResultdata->primaryPhy =
		info->primary_phy; //todo check converge OHOS_BLE_SCAN_PHY_2M
	pscanResultdata->secondaryPhy =
		info->secondary_phy; //todo check converge OHOS_BLE_SCAN_PHY_1M
	// pscanResultdata->dataStatus //todo
	pscanResultdata->directAddrType; //todo
	pscanResultdata->directAddr; //todo
	pscanResultdata->advLen; //todo important
	pscanResultdata->advData; //todo important

	do {
		switch (info->addr->type) {
		case BT_ADDR_LE_PUBLIC:
			pscanResultdata->addrType =
				OHOS_BLE_PUBLIC_DEVICE_ADDRESS;
			break;
		case BT_ADDR_LE_RANDOM:
			pscanResultdata->addrType =
				OHOS_BLE_RANDOM_DEVICE_ADDRESS;
			break;
		case BT_ADDR_LE_PUBLIC_ID:
			pscanResultdata->addrType =
				OHOS_BLE_PUBLIC_IDENTITY_ADDRESS;
			break;
		case BT_ADDR_LE_RANDOM_ID:
			pscanResultdata->addrType =
				OHOS_BLE_RANDOM_STATIC_IDENTITY_ADDRESS;
			break;
		default:
			// pscanResultdata->addrType = OHOS_BLE_UNRESOLVABLE_RANDOM_DEVICE_ADDRESS;
			pscanResultdata->addrType = OHOS_BLE_NO_ADDRESS;
			break;
		}
		if (bt_addr_le_cmp(info->addr, BT_ADDR_LE_ANY) == 0) {
			pscanResultdata->addrType = OHOS_BLE_NO_ADDRESS;
		}
	} while (0);

	do {
		uint8_t map[2][2][2][2] = {}; //ext conn scan dir
		memset(map, -1, sizeof(map));
		map[0][0][0][0] = OHOS_BLE_EVT_LEGACY_NON_CONNECTABLE;
		map[0][0][1][0] = OHOS_BLE_EVT_LEGACY_SCANNABLE;
		// map[0][1][1][0] = OHOS_BLE_EVT_LEGACY_SCANNABLE; //3
		map[0][1][1][0] = OHOS_BLE_EVT_LEGACY_CONNECTABLE; //3
		map[0][1][0][1] = OHOS_BLE_EVT_LEGACY_CONNECTABLE_DIRECTED;
		map[1][0][0][0] = OHOS_BLE_EVT_NON_CONNECTABLE_NON_SCANNABLE;
		map[1][0][0][1] =
			OHOS_BLE_EVT_NON_CONNECTABLE_NON_SCANNABLE_DIRECTED;
		map[1][1][0][0] = OHOS_BLE_EVT_CONNECTABLE;
		map[1][1][1][0] = OHOS_BLE_EVT_CONNECTABLE; //2
		map[1][1][0][1] = OHOS_BLE_EVT_CONNECTABLE_DIRECTED;
		map[1][1][1][1] = OHOS_BLE_EVT_CONNECTABLE_DIRECTED; //1
		map[1][0][1][0] = OHOS_BLE_EVT_SCANNABLE;
		// map[1][1][1][0] = OHOS_BLE_EVT_SCANNABLE;  //2
		map[1][0][1][1] = OHOS_BLE_EVT_SCANNABLE_DIRECTED;
		// map[1][1][1][1] = OHOS_BLE_EVT_SCANNABLE_DIRECTED; //1

		uint8_t e = 0;
		uint8_t c = 0;
		uint8_t s = 0;
		uint8_t d = 0;

		e = (info->adv_props & BT_GAP_ADV_PROP_EXT_ADV) != 0 ? 1 : 0;
		c = (info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) != 0 ? 1 :
										 0;
		s = (info->adv_props & BT_GAP_ADV_PROP_SCANNABLE) != 0 ? 1 : 0;
		d = (info->adv_props & BT_GAP_ADV_PROP_DIRECTED) != 0 ? 1 : 0;

		if ((info->adv_props & BT_GAP_ADV_PROP_SCAN_RESPONSE) != 0) {
			pscanResultdata->eventType =
				OHOS_BLE_EVT_LEGACY_SCAN_RSP_TO_ADV_SCAN;
			pscanResultdata->eventType =
				OHOS_BLE_EVT_LEGACY_SCAN_RSP_TO_ADV;
		}
		if (map[e][c][s][d] != -1) {
			pscanResultdata->eventType = map[e][c][s][d];
		}
	} while (0);

	return 0;
}

#endif // __XR_OHOS_UTILS_H__